;
;  PROGRAM:  PRINT III (PRINT3)
;  VERSION:  1.3
;  DATE:  17 May 83
;  AUTHOR:  RICHARD CONN
;  PREVIOUS VERSIONS:  1.2 (26 Apr 83), 1.1 (26 Apr 83), 1.0 (22 Apr 83)
;
VERS	equ	13

FALSE	equ	0
TRUE	equ	NOT FALSE

timeok	equ	FALSE	;TRUE to enable TIME feature, FALSE to disable

;
;	PRINT3 is THE file print utility for ZCPR2.  Installable by GENINS,
; PRINT3 provides a wide range of options for the user.  First, PRINT3 allows
; the user to employ wild cards and file name lists (lists of files separated
; by commas, like: file1,file2,file3,...).  Second, PRINT3 provides the
; following options:
;		E	Exact Mode
;				No heading appears, no line numbering,
;				no page numbering, tab expansion, form
;				feeds
;		F	File Name Toggle
;				The Heading Line can optionally contain
;				the name of the file
;		H@head@	Heading Text
;				The user may specify the text of the heading
;				to appear at the top of every page
;		I	Inspect Files
;				The user approves each file to be printed
;				before the printing process begins
;		L	Toggle Line Numbering
;				Each line may or may not begin with a line
;				number
;		M	Multiple Runs Toggle
;				The user may or may not be prompted to set
;				the Top of Form on his printer; Multiple Runs
;				ON means that he will not be prompted
;		N	Toggle Page Numbering
;				The numbering of each page is turned on or
;				off
;		Onn	Offset Each Line
;				Move each line in the indicated number of
;				characters from the left
;		Snnnn	Skip to Specified Page
;				Printing begins on the indicated page
;		T	Toggle Time Display
;				Time/Date information is optionally included
;				in the page header
;


;
;	This program is Copyright (c) 1982, 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  BASIC SYSLIB ROUTINES NEEDED BY TEMPLATE
;
ESIZE	EQU	16	; SIZE OF DIR ENTRY (FROM SYSLIB DIRF ROUTINE)

	EXT	DIRFS	; DIRECTORY PROCESSOR
	EXT	DIRPACK	; PACK DIRECTORY

	EXT	ZGPINS	; INIT BUFFERS
	EXT	ZFNAME	; FILE NAME PROCESSOR

	EXT	INITFCB	; INIT FCB
	EXT	RETUD	; RETURN CURRENT USER/DISK
	EXT	PUTUD	; SAVE CURRENT USER/DISK
	EXT	GETUD	; RESTORE CURRENT USER/DISK
	EXT	LOGUD	; LOG INTO USER/DISK
	EXT	PRINT	; PRINT STRING PTED TO BY RET ADR
	EXT	PADC	; PRINT A IN DEC
	EXT	COUT	; CONSOLE OUTPUT ROUTINE
	EXT	CONDIN	; CONDITIONAL INPUT ROUTINE
	EXT	CIN	; CONSOLE INPUT ROUTINE
	EXT	CAPS	; CAPITALIZE ROUTINE
	EXT	CRLF	; NEW LINE ROUTINE
	EXT	CLINE	; COMMAND LINE STRING SAVE ROUTINE
	EXT	CODEND	; CODE END COMPUTATION ROUTINE

	EXT	F$OPEN	; FILE OPEN
	EXT	F$READ	; BLOCK READ
	EXT	F$CLOSE	; FILE CLOSE

	EXT	EVAL10	; STRING TO BINARY CONVERSION
	EXT	LHLDC	; LST: HL AS DEC OUTPUT
	EXT	LPSTR	; LST: (HL) STRING OUTPUT
	EXT	LPRINT	; LST: STRING OUTPUT
	EXT	LCRLF	; LST: NEW LINE
	EXT	LOUT	; LST: OUTPUT
	EXT	MOVEB	; MOVEB ROUTINE

	if	timeok
	EXT	TIME	; TIME Library Module Routine
	endif

;
;  CP/M EQUATES
;
CPM	EQU	0	; WARM BOOT
BDOSE	EQU	CPM+5	; BDOS ENTRY
FCB	EQU	CPM+5CH	; FCB
TBUFF	EQU	CPM+80H	; INPUT LINE BUFFER
DEL	EQU	7FH	; <DEL>
CR	EQU	13	; <CR>
FF	EQU	12	; <FF>
LF	EQU	10	; <LF>
CTRLC	EQU	'C'-'@'	; ^C
CTRLG	EQU	'G'-'@'
CTRLH	EQU	'H'-'@'
CTRLI	EQU	'I'-'@'
CTRLX	EQU	'X'-'@'
CTRLZ	EQU	'Z'-'@'

;
;  OTHER EQUATES
;
EOLD	EQU	0FFH	; END OF LOAD DELIMITER

;
;  Branch to Start of Program
;
	JMP	START

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	11	; CLASS 11
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  **** Special Initial Value Area for GENINS
;
LWIDTH:
	DB	132	; WIDTH OF LINE
LTPP:
	DB	44	; LINES OF TEXT PER PAGE
LSPP:
	DB	5	; LINES TO SKIP PER PAGE
CWIDTH:
	DB	80	; WIDTH OF SCREEN
CTPP:
	DB	22	; LINES OF TEXT PER SCREEN
CSPP:
	DB	1	; LINES TO SKIP PER SCREEN
;
;  NOTE:  LTPP + LSPP + 2 (HEADER SIZE) = TOTAL LINES PER PAGE ON PRINTER
;  NOTE:  CTPP + CSPP + 1 (FOOTER SIZE) = TOTAL LINES PER SCREEN ON CONSOLE
;
DLNUMFL:
	DB	0	; LINE NUMBER FLAG (DEFAULT TO NO)
DPNUMFL:
	DB	0FFH	; PAGE NUMBER FLAG (DEFAULT TO YES)
DEXACT:
	DB	0	; EXACT PRINT FLAG (DEFAULT TO NO)
DFNPFL:
	DB	0FFH	; FILE NAME PRINT FLAG (DEFAULT TO YES)
DTIMEPFL:
	DB	0FFH	; TIME PRINT FLAG (DEFAULT TO YES)
DMULTFL:
	DB	0FFH	; MULTIPLE RUN FLAG (DEFAULT TO YES)
DINSPECT:
	DB	0	; INSPECT FILES (DEFAULT TO NO)
DOFFSET:
	DB	0	; INITIAL COLUMN OFFSET (DEFAULT TO NO COLUMNS)
;
;  OTHER BUFFERS
;
SKIPFL:
	DB	0	; SKIP FLAG (DEFAULT TO NO)
SKIPNUM:
	DS	2	; PAGE NUMBER TO SKIP TO
LNUM:
	DS	2	; CURRENT LINE NUMBER
PNUM:
	DS	2	; CURRENT PAGE NUMBER
HBUF:
	DS	100	; BUFFER FOR HEADING
;
	if	timeok
TIMEBF:
	DS	100	; BUFFER FOR TIME STAMP
	endif
;
LNUMFL:
	DB	0	; LINE NUMBER FLAG (DEFAULT TO NO)
PNUMFL:
	DB	0FFH	; PAGE NUMBER FLAG (DEFAULT TO YES)
EXACT:
	DB	0	; EXACT PRINT FLAG (DEFAULT TO NO)
FNPFL:
	DB	0FFH	; FILE NAME PRINT FLAG (DEFAULT TO YES)
TIMEPFL:
	DB	0FFH	; TIME PRINT FLAG (DEFAULT TO YES)
MULTFL:
	DB	0FFH	; MULTIPLE RUN FLAG (DEFAULT TO YES)
INSPECT:
	DB	0	; INSPECT FILES (DEFAULT TO NO)
OFFSET:
	DB	0	; OFFSET ON PAGE

;
;  Start of Program
;
START:
	LXI	H,0	; GET STACK PTR
	DAD	SP
	SHLD	STACK	; SAVE IT
	LXI	SP,STACK	; SET SP
	CALL	PUTUD	; SAVE CURRENT USER/DISK AWAY
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; SAVE DISK
	STA	DISK
	MOV	A,C	; SAVE USER
	STA	USER

	CALL	ZGPINS	; INIT BUFFERS

	LXI	H,TBUFF	; SAVE COMMAND LINE
	CALL	CLINE
	SHLD	CMDLNE	; SAVE PTR

;
;  **** Banner of Program
;
	CALL	PRINT
	DB	'PRINT III,  Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0',0

;
;  Check for Help Request
;
	LDA	FCB+1	; GET FIRST CHAR OF FILE NAME
	CPI	' '	; NO FILE SPEC?
	JZ	HELP
	CPI	'/'	; OPTION CAUGHT?
	JNZ	ECONT

;
;  **** Print Help Information
;
HELP:
	CALL	PRINT
	DB	CR,LF,'PRINT III Command --'
	db	cr,lf,'	PRINT III is invoked by the following command line:'
	db	cr,lf,'		PRINT file1,file2,...,filen o...'
	db	cr,lf,'where each "filen" is an ambiguous file name and type'
	db	cr,lf,'and "o" is zero or more of the following options:'
	db	cr,lf,'	E	Exact Print (expand tabs, form feed, no line'
	db	cr,lf,'			or page numbers, no heading)'
	db	cr,lf,'	F	Toggle File Name Display'
	db	cr,lf,'	H@head@	Specify Page Heading (@ is any printing char)'
	db	cr,lf,'	I	Inspect and Select Files First'
	db	cr,lf,'	L	Toggle Numbering of Each Line'
	db	cr,lf,'	M	Toggle Multiple Runs (MR=No TOF Msg)'
	db	cr,lf,'	N	Toggle Numbering of Each Page'
	db	cr,lf,'	Onn	Offset Printout by nn Characters from Left'
	db	cr,lf,'	Snnnn	Skip to Specified Page before Printing'
;
	if	timeok
	db	cr,lf,'	T	Toggle Time Display (if available) in header'
	endif
;
	db	cr,lf,'Examples:'
	db	cr,lf,'		PRINT MYFILE.TXT,*.MAC LH''SAMPLE'''
	db	cr,lf,'			-- Number Lines, SAMPLE is Heading'
	db	cr,lf,'		PRINT MYFILE.* S25E'
	db	cr,lf,'			-- Skip to Page 25, Exact Print'
	db	cr,lf,'	At any time, ^C aborts PRINT III and ^X skips to next '
	db	'file'
	DB	CR,LF,0

;
;  RETURN TO OS
;
RETURN:
	LHLD	STACK	; GET OLD STACK
	SPHL		; SET IT
	RET

;
;  PROGRAM'S INIT ROUTINE
;
ECONT:
	CALL	INIT	; PROG INIT ROUTINE
;
;  EXTRACT FLAGS IF PRESENT
;
	LXI	H,0	; SET FILE COUNT
	SHLD	FILECNT
	LHLD	CMDLNE	; PT TO BUFFER
;
;  SKIP TO FILE NAME STRING
;
	CALL	SBLANK	; SKIP OVER BLANKS
;
;  SKIP TO END OF FILE NAME STRING
;
	CALL	SNBLANK	; SKIP OVER NON-BLANKS
;
;  CHECK FOR LEADING SLASH ON OPTION AND SKIP IT IF SO
;
OPT:
	CPI	'/'	; OPTION CHAR?
	JNZ	OPTION
	INX	H	; SKIP SLASH
;
;  PROCESS LIST OF OPTIONS
;
OPTION:
	MOV	A,M	; GET BYTE
	ORA	A	; DONE?
	JZ	DSPEC
	INX	H	; PT TO NEXT CHAR
	CPI	' '	; SKIP OVER SPACES
	JZ	OPTION
	MOV	C,A	; COMMAND IN C
	LXI	D,OPTAB	; PT TO OPTION TABLE
OPTL:
	LDAX	D	; GET OPTION LETTER
	ORA	A	; END OF TABLE?
	JZ	HELP	; HELP IF SO
	CMP	C	; MATCH?
	JZ	OPTM	; PROCESS IF SO
	INX	D	; PT TO NEXT ENTRY
	INX	D
	INX	D
	JMP	OPTL
;
;  PROCESS OPTION
;
OPTM:
	PUSH	H	; SAVE HL ON STACK
	LXI	H,OPTION	; GET RETURN ADDRESS
	XTHL		; ... ON STACK AND RESTORE HL
	INX	D	; PT TO ADDRESS
	LDAX	D	; GET ADDRESS LOW
	MOV	B,A	; ... IN B
	INX	D
	LDAX	D	; GET ADDRESS HIGH
	MOV	D,A	; ... IN D
	MOV	E,B	; LOW IN E
	PUSH	D	; PUT ADDRESS ON STACK
	MOV	A,C	; COMMAND IN A
	RET		; "CALL" OPTION ROUTINE

;
;  **** PROGRAM INIT ROUTINE
;	THIS ROUTINE IS USED BY THE PROGRAM TO PERFORM ANY INITS
;
INIT:
	lxi	h,dlnumfl	;copy defaults into buffers
	lxi	d,lnumfl
	mvi	b,8		;8 bytes
	call	moveb		;do copy
	xra	a	;A=0
	sta	skipfl	;set no skip
	sta	hbuf	;set no heading
;
	if	timeok
	call	time	;get time string
	lxi	d,timebf	;store in buffer
initt:
	mov	a,m	;get byte
	stax	d	;put byte
	inx	h	;pt to next
	inx	d
	ora	a	;done?
	jnz	initt
	endif
;
	RET

;
;  **** OPTION TABLE
;	EACH OPTION IS A CAPITAL LETTER OR SPECIAL CHAR FOLLOWED BY
;		AN ADDRESS; THE TABLE IS TERMINATED BY A BINARY ZERO
;
OPTAB:
	db	'E'
	dw	optexact
	db	'F'
	dw	optfn
	db	'H'
	dw	opthd
	db	'I'
	dw	optinsp
	db	'L'
	dw	optln
	db	'M'
	dw	optmult
	db	'N'
	dw	optpn
	db	'O'
	dw	optoffs
	db	'S'
	dw	optskip
;
	if	timeok
	db	'T'
	dw	opttime
	endif
;
	DB	0	; END OF TABLE
;
;  Option:  E (Toggle exact mode)
;
optexact:
	lda	exact	;get flag
	cma		;flip it
	sta	exact	;put flag
	ret
;
;  Option:  F (Toggle file name display)
;
optfn:
	lda	fnpfl	;get flag
	cma		;flip it
	sta	fnpfl	;put flag
	ret
;
;  Option:  H (Set Heading)
;
opthd:
	lxi	d,hbuf	;pt to heading buffer
	mov	a,m	;get delim
	ora	a	;none?
	rz
	mov	b,a	;delim in B
	inx	h	;pt to next char
opthd1:
	mov	a,m	;get next char
	ora	a	;done?
	jz	opthd3
	cmp	b	;done by trailing delim?
	jz	opthd2
	stax	d	;save char
	inx	h	;pt to next
	inx	d
	jmp	opthd1
opthd2:
	inx	h	;skip over delim
opthd3:
	xra	a	;store ending 0
	stax	d
	ret
;
;  Toggle Inspect Option
;
optinsp:
	lda	inspect	;flip flag
	cma
	sta	inspect
	ret
;
;  Set Line Number Flag
;
optln:
	lda	lnumfl	;flip flag
	cma
	sta	lnumfl
	ret
;
;  Set Multiple Run Flag
;
optmult:
	lda	multfl	;flip flag
	cma
	sta	multfl
	ret
;
;  Set Page Numbering Flag
;
optpn:
	lda	pnumfl	;flip flag
	cma
	sta	pnumfl
	ret
;
;  Set Page Offset
;
optoffs:
	call	eval10	;get number
	mov	a,e	;get low-order byte
	sta	offset	;set offset
	ret
;
;  Set Skip Flag and get number
;
optskip:
	mvi	a,0ffh	;set flag
	sta	skipfl
	call	eval10	;get number
	xchg
	shld	skipnum	;set page number to skip to
	xchg		;HL pts to next char
	mov	a,d	;see if page number was zero
	ora	e
	rnz
	xra	a	;if zero, just turn off skip flag
	sta	skipfl
	ret
;
	if	timeok
;
;  Set Time Flag
;
opttime:
	lda	timepfl	;flip flag
	cma
	sta	timepfl
	ret
;
	endif
;
;  BEGIN MOVING THROUGH FILE NAMES, SEPARATED BY COMMAS
;
DSPEC:
	LHLD	CMDLNE	; PT TO FIRST BYTE
	CALL	SBLANK	; SKIP TO NON-BLANK
;
;  MAJOR REENTRY POINT WHEN FILE SPECS ARE SEPARATED BY COMMAS
;    HL PTS TO FIRST BYTE OF NEXT FILE SPEC
;
DSPEC1:
	LXI	SP,STACK	; RESET STACK
	CALL	GETUD	; RESET USER IF NECESSARY
	LXI	D,NTFCB	; PT TO FCB IN DE, PT TO FIRST CHAR OF FILE NAME IN HL

	CALL	ZFNAME	; EXTRACT FILE NAME INTO FCB, AND GET DISK AND USER
	JZ	DERR	; ERROR HANDLER

	SHLD	NEXTCH	; SAVE PTR TO DELIMITER WHICH ENDED SCAN
	MOV	A,B	; SAVE POSSIBLE DRIVE SPEC
	CPI	0FFH	; CURRENT DISK?
	JZ	DSPEC2
	LDA	MDISK	; GET MAX DISK NUMBER
	DCR	B	; ADJUST TO WITHIN BOUNDS 0-15
	CMP	B	; WITHIN BOUNDS?
	MOV	A,B	; GET DISK NUMBER IN A
	JNC	USPEC
DERR:
	CALL	PRINT
	DB	CR,LF,'Invalid Disk Specification',0
	JMP	DRETURN

;
;  SET CURRENT DISK
;
DSPEC2:
	LDA	DISK	;GET CURRENT DISK
	MOV	B,A	;... IN B

;  CHECK FOR USER NUMBER
USPEC:
	MOV	A,C	; GET NEW USER NUMBER
	CPI	0FFH	; DEFAULT USER?
	JZ	USPEC1
	CPI	'?'	; ALL USERS NOT ALLOWED?
	JZ	UERR
	LDA	MUSER	; GET MAX USER NUMBER
	CMP	C
	MOV	A,C	; USER NUMBER IN A
	JNC	FCT
UERR:
	CALL	PRINT
	DB	CR,LF,'Invalid User Number',0
	JMP	DRETURN
USPEC1:
	LDA	USER	;GET CURRENT USER
	MOV	C,A	;... IN C

;
;  LOAD DIRECTORY AND PERFORM FUNCTION
;
FCT:
	MOV	A,B	; SAVE NEW DISK/USER AWAY
	STA	CDISK	; CURRENT DISK
	MOV	A,C
	STA	CUSER	; CURRENT USER
	CALL	LOGUD	; LOG INTO ACCOUNT
	CALL	CODEND	; PT TO END OF CODE
	CALL	RETUD	; GET USER NUMBER FOR DIRFS
	MVI	A,11000000B	; SELECT SYS AND NON-SYS FILES
	ORA	C	; OR IN USER NUMBER
	LXI	D,NTFCB	; PT TO FCB
	CALL	INITFCB	; INIT THE FCB
	CALL	DIRFS	; LOAD DIR, SELECT FILES, PACK, AND ALPHABETIZE
;
;  DETERMINE BEGINNING OF SCRATCH AREA (SCRATCH) AND SZIE IN PAGES (BCNT)
;
	PUSH	H	; SAVE PTR AND COUNT
	PUSH	B
	LXI	D,ESIZE	; SET PTR TO NEXT FREE BLOCK
FCTFRE:
	MOV	A,B	; DONE?
	ORA	C
	JZ	FCTFR1
	DAD	D	; PT TO NEXT
	DCX	B	; COUNT DOWN
	JMP	FCTFRE
FCTFR1:
	INR	H	; NEXT PAGE
	MVI	L,0
	SHLD	SCRATCH	; SET PTR TO SCRATCH AREA
	XCHG		; PTR IN DE
	LHLD	BDOSE+1	; COMPUTE BLOCK BUFFER SIZE
	MOV	A,H	; ADJUST FOR ZCPR2
	SUI	10
	SUB	D	; A=SIZE IN BLOCKS
	STA	BCNT	; SET BLOCK COUNT
	POP	B	; RESTORE AND SAVE REGS
	POP	H
;
;  ALLOW USER TO INSPECT FILES
;
	PUSH	H
	PUSH	B
	CALL	ICHECK	; CHECK FOR INSPECT OPTION AND INSPECT IF SET
	POP	B	; RESTORE COUNT AND PTR
	POP	H

;
;  PERFORM FUNCTION; HL PTS TO FILE AND BC CONTAINS NUMBER OF FILES
;
FCTL:
	MOV	A,B		; CHECK FOR COMPLETION (COUNT = 0)
	ORA	C
	JZ	FCTL1
	DCX	B		; COUNT DOWN
	LXI	SP,STACK	; RESET STACK
	PUSH	B		; SAVE COUNT AND POINTER
	PUSH	H
	CALL	FUNCTION	; PERFORM FUNCTION
FCTLNXT:
	LXI	SP,STACK-4	; RESTORE STACK
	POP	H		; GET PTR AND COUNT
	POP	B
	LXI	D,ESIZE		; PT TO NEXT ENTRY
	DAD	D
	JMP	FCTL

;
;  CHECK FOR NEXT FILE SPEC
;
FCTL1:
	CALL	GETUD	; RETURN TO HOME USER/DISK
	LHLD	NEXTCH	; GET PTR
	MOV	A,M	; GET DELIM
	CPI	','	; ANOTHER FILE?
	JNZ	DRETURN
	INX	H	; PT TO CHAR AFTER COMMA
	JMP	DSPEC1	; CONTINUE PROCESSING

;
;  **** EMERGENCY ABORT
;
ABORT:
	CALL	PRINT
	DB	CR,LF,'** PRINT III Abort **',CR,LF,0
	CALL	GETUD	; RETURN HOME AND FALL THRU TO DRETURN
;
;  **** FUNCTION COMPLETE -- CLEANUP AND EXIT
;	FILL THIS IN WITH CLEANUP CODE FOR EXIT
;
DRETURN:
	JMP	RETURN

;
;  **** INSPECT FILES -- THIS ROUTINE IS TO PERFORM A FILE INSPECTION
;	ON INPUT, HL PTS TO FIRST 16-BYTE ENTRY AND BC=NUMBER OF ENTRIES
;
ICHECK:
	mov	a,b	;any files?
	ora	c
	rz
	push	h	;save ptrs
	push	b
	lxi	d,esize	;size of entry
ichk1:
	mvi	m,0	;clear MSBytes
	dad	d	;pt to next
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichk1
	pop	b	;restore ptrs
	pop	h
	lda	inspect	;inspect?
	ora	a	;0=no
	rz
	call	print
	db	cr,lf,'PRINT III File Inspect Mode',0
ichk2:
	call	print
	db	cr,lf,'Select ',0
	call	prfn	;print file name
	call	print
	db	' -- (Y/N/Q=Select Rest/S=Skip Rest/other=Y)? '
	db	0
	call	cin	;get response
	call	caps	;capitalize
	call	cout	;echo
	cpi	'Q'	;select rest?
	jz	ichkyr
	cpi	'S'	;skip rest
	jz	ichknr
	cpi	'N'	;no to this one?
	jnz	ichk3
	mvi	m,0ffh	;set NO flag in file FCB
ichk3:
	dad	d	;pt to next one
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichk2
	RET
;  Check Rest of Files as Selected
ichkyr:
	call	print
	db	cr,lf,'	Rest of Files Selected',0
	ret
;  Check Rest of Files as NOT Selected
ichknr:
	mvi	m,0ffh	;set NO flag
	dad	d	;pt to next
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichknr
	call	print
	db	cr,lf,'	Rest of Files NOT Selected',0
	ret
;
;  **** FUNCTION -- MAIN FUNCTION OF TEMPLATE
;	ON ENTRY, HL PTS TO NAME OF FILE (16 BYTES) AND USER IS LOGGED INTO
;		DIRECTORY CONTAINING INDICATED FILE
;
FUNCTION:
;
;  FILE PRINT Routine -- Print the File Whose Name is Pointed to by
;	HL; we are already logged into the correct directory
;
	mov	a,m	;file selected?
	ora	a	;0=yes
	rnz
	lda	multfl	;multiple runs?
	ora	a	;0=no
	jnz	fprint	;go right into function
	call	print
	db	cr,lf,'File: ',0
	call	prfn	;print file name
	call	print
	db	' -- Please Set Top of Form'
	db	cr,lf,'	Strike Any Key When Ready ',0
	call	cin	;get response
	cpi	ctrlc	;abort?
	jz	abort
	cpi	ctrlx
	rz
fprint:
	call	prinit	;init print buffers
	call	fload	;load buffer initially
	call	prhead	;print heading line
	lhld	scratch	;pt to first char in file
	shld	nxtln	;set pointer to next line
fprloop:
	call	prline	;print line of file
	jnz	fprloop	;done if EOF
	lda	ltpp	;check for new page
	mov	b,a
	lda	lcount	;LTPP and Line Count are Equal if New Page
	cmp	b	;equal?
	jnz	fprlp1
	call	lcrlf	;advance 2 lines
	call	lcrlf
fprlp1:
	call	page	;advance to top of next page
	ret
;
;  Init Print Buffers and Print File Name
;
prinit:
	lxi	d,tfcb	;set up FCB
	mvi	b,12	;12 bytes
	call	moveb
	lxi	h,0	;HL=0
	shld	lnum	;set line number
	inx	h	;HL=1
	shld	pnum	;set page number
	lda	ltpp	;set line count
	sta	lcount
	call	print
	db	cr,lf,'Printing File ',0
	lxi	h,tfcb	;pt to FCB
	call	prfn	;print file name
	ret
;
;  FILE LOAD (FLOAD) Routine -- Initial Load of memory buffer
;
fload:
	lxi	d,tfcb	;pt to file fcb
	call	initfcb	;init file's fcb
	call	f$open	;open file for input
	jz	fload1	;open was OK
	call	print
	db	cr,lf,'File ',0
	xchg		;HL pts to FCB
	call	prfn	;print file name
	call	print
	db	' NOT Found',0
	pop	d	;clear return address
	ret		;abort printout of this file
;
;  This is an entry point for further memory loads of the file
;
fload1:
	lda	bcnt	;get number of blocks to load
	mov	c,a	;... in C
	lhld	scratch	;get address of first block to load into
	shld	nxtblk	;set pointer to next block to load
fload2:
	call	rdblk	;read a block (128 bytes)
	jnz	eof	;eof encountered?
	call	rdblk	;read another block (128 bytes)
	jnz	eof	;eof encountered?
	dcr	c	;count down
	jnz	fload2
	lhld	nxtblk	;pt to next byte to load
	mvi	m,eold	;mark end of load
	ret
eof:
	lxi	d,tfcb	;close file
	call	f$close
	lhld	nxtblk	;ensure ^Z
	mvi	m,ctrlz
	ret
rdblk:
	lxi	d,tfcb	;pt to FCB
	call	f$read	;read next block
	ora	a	;error?
	rnz
	lhld	nxtblk	;get ptr to next block
	xchg		; as dest
	lxi	h,tbuff	;ptr to DMA address
	mvi	b,128	;copy 128 bytes
rdblk1:
	mov	a,m	;get byte
	ani	7fh	;mask out msb
	stax	d	;put byte
	inx	h	;pt to next
	inx	d
	dcr	b	;count down
	jnz	rdblk1
	xchg		;new nxtblock
	shld	nxtblk
	ret

;
;  Line Print Routine
;	Print Next Line with Optional Disk Load
;	Input Parameter is NXTLN, which is the address of the first char
; on the next line
;	Output Parameter is Zero Flag, with Z meaning done with print, NZ
; meaning more yet to print
;
prline:
	call	proffs	;print offset
	lhld	lnum	;increment line number
	inx	h
	shld	lnum
	lhld	nxtln	;pt to first char of next line
	mvi	c,0	;init char count
	mov	a,m	;get first char of line
	cpi	ctrlz	;EOF?
	cnz	prlnum	;print line number (optional)
prl1:
	mov	a,m	;get char
	cpi	eold	;end of load?
	jz	prload
	cpi	ctrlz	;eof?
	jz	prexit
	inx	h	;pt to next char
	cpi	ctrli	;tab?
	jz	prtab
	cpi	cr	;<CR>?
	jz	prcr
	cpi	ff	;form feed?
	jz	prff
	cpi	lf	;end of line?
	jz	prldn
	cpi	ctrlh	;back space?
	jz	prbs
	cpi	ctrlg	;ring bell?
	jz	prbell
	cpi	del	;delete char?
	jz	prl1	;skip it
	cpi	' '	;other control char?
	jc	prl1	;skip if other control char
	call	prout	;print char
	inr	c	;increment char count
	call	eoltest	;check to see if at end of line and newline if so
	jmp	prl1
;
;  End of Load Reached -- Load More of File from Disk
;
prload:
	push	b	;save char count
	call	fload1	;use load routine
	pop	b	;get char count
	lhld	scratch	;next byte is here
	jmp	prl1	;continue processing
;
;  Tabulate
;
prtab:
	mvi	a,' '	;space
	call	prout
	inr	c	;new char
	call	eoltest	;process EOL
	mov	a,c	;done?
	ani	7
	jnz	prtab	;continue tabulation
	jmp	prl1	;continue processing
;
;  Exit with Zero Flag Set if Done
;
prexit:
	xra	a	;set zero flag
	ret
;
;  Carriage Return -- Reset Character Count and Continue
;
prcr:
	call	prout	;send CR to printer
	mvi	c,0	;reset char count
	jmp	prl1	;continue processing
;
;  Form Feed -- Advance to Top of Next Page
;
prff:
	call	page	;page eject with heading
	mvi	c,0	;reset char count
	jmp	prl1	;continue processing
;
;  Line Feed -- End of Routine
;
prldn:
	call	prout	;echo LF to printer
	shld	nxtln	;set ptr to first char of next line
	mvi	a,0ffh	;set not done
	ora	a	;set flags
	ret
;
;  Backspace on Printer
;
prbs:
	mov	a,c	;check for beginning of line
	ora	a
	jz	prl1	;continue if at BOL
	mvi	a,ctrlh	;backspace
	call	prout
	dcr	c	;back up char position
	jmp	prl1	;continue
;
;  Ring Bell on Printer
;
prbell:
	call	prout	;ring the bell
	jmp	prl1	;continue without advancing char position
;
;  Test for End of Line and Process if so
;
eoltest:
	lda	offset	;get offset
	mov	b,a	;... in B
	lda	lwidth	;get line width
	sub	b	;compute remaining width
	sui	4	;4 chars less for continuation mark
	mov	b,a	;result in B
	lda	lnumfl	;line numbering (lines are 7 chars shorter if so)
	ora	a	;0=no
	jz	eolt1
	mov	a,b	;reduce by 7 for line numbers
	sui	7
	mov	b,a
eolt1:
	mov	a,b	;get line width
	cmp	c	;there?
	rnz		;continue if not
	mov	a,m	;get next char
	cpi	cr	;new line next?
	rz		;continue if so
	cpi	ctrlh	;backspace next?
	rz		;continue if so
	mvi	a,' '	;print continuation chars
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,cr	;new line
	call	prout
	mvi	a,lf
	call	prout
	mvi	c,0	;reset char position
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;printing line numbers?
	ora	a	;0=no
	rz
	call	lprint
	db	'     : ',0
	ret
;
;  Output a character to the printer
;	A = Character
;
prout:
	mov	b,a	;char in B
	call	condin	;check for abort
	jz	prout1
	cpi	ctrlc	;abort?
	jz	abort
	cpi	ctrlx	;abort this one file?
	jz	cxabort
prout1:
	lda	skipfl	;skipping?
	ora	a	;set flags (Z=no skip=print char)
	mov	a,b	;restore char
	cz	lout	;send character to printer
	cpi	lf	;special tests if it is a line feed
	rnz		;done if non-LF char
	lda	lcount	;decrement line counter
	dcr	a
	sta	lcount
	rnz
;
;  Paging Required
;	Skip to top of next page; reset LCOUNT (Lines Left on Page Count);
;	print header
;
prout0:
	lda	ltpp	;get number of text lines per page
	sta	lcount	;set as new line count
	push	h	;save ptr
	lhld	pnum	;increment page number
	inx	h
	shld	pnum
	lda	lspp	;get number of lines to skip per page
	call	lineskp	;skip lines
	pop	h	;restore ptr
	mov	a,m	;check next character
	cpi	ctrlz	;EOF?
	jnz	prhead	;print 2-line heading if NOT EOF
	ret
;
;  Abort current file with final page eject
;
cxabort:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	lda	lspp	;number of lines to skip per page
	call	lineskp	;skip lines
	jmp	fctlnxt	;continue with next file
;
;  Skip out rest of page
;	Form Feed Function
;
page:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	jmp	prout0	;process top of new page
;
;  Skip out lines on page
;	A = number of lines to skip
;
lineskp:
	mov	b,a	;line count in B
	ora	a	;any?
	rz
	lda	skipfl	;skipping?
	ora	a
	rnz
lines1:
	mvi	a,cr	;output new line to printer
	call	lout
	mvi	a,lf
	call	lout
	dcr	b	;count down
	jnz	lines1
	ret
;
;  Print Line Number (optional)
;
prlnum:
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;get flag
	ora	a	;0=don't number lines
	rz
	push	h	;save ptr
	lhld	lnum	;get line number
	call	lhldc	;print line number
	call	lprint	;print separator
	db	': ',0
	pop	h	;restore ptr
	ret
;
;  Print 2-line heading and control skipping
;
prhead:
	push	h	;save ptr
	lda	skipfl	;currently skipping?
	ora	a	;0=no
	cnz	skiptst	;test for shut off
	lda	exact	;exact says no heading
	ora	a	;0FFH=yes
	jnz	prhead1
	call	proffs	;print offset
	lda	pnumfl	;number pages?
	ora	a	;0=no
	cnz	prpnum	;print page heading and number
	lda	fnpfl	;print file name?
	ora	a	;0=no
	cnz	prfname	;print file name
;
	if	timeok	;time available?
	lda	timepfl	;print time?
	ora	a	;0=no
	cnz	prtime	;print time
	endif
;
	lda	hbuf	;print heading?
	ora	a	;0=no
	cnz	prhdg	;print heading
prhead1:
	pop	h	;restore ptr
prhead2:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lcrlf	;new line
	jmp	lcrlf
;
;  Test for completion of skipping
;
skiptst:
	lhld	pnum	;get page number
	xchg		;... in DE
	lhld	skipnum	;get page to skip to
	mov	a,h	;compare them
	cmp	d
	rnz
	mov	a,l
	cmp	e
	rnz
	xra	a	;A=0 to stop skipping
	sta	skipfl	;set flag
	ret
;
;  Print Line Offset
;
proffs:
	lda	skipfl	;skipping?
	ora	a
	rnz
	push	b	;save BC
	lda	offset	;get offset
	ora	a	;any?
	jz	proff2
	mov	c,a	;offset in B
proff1:
	mvi	a,' '	;space over
	call	prout
	dcr	c	;count down
	jnz	proff1
proff2:
	pop	b
	ret
;
;  Print Page Number
;
prpnum:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lprint	;print header
	db	'Page ',0
	lhld	pnum	;print current page number
	call	lhldc	;print as decimal
	ret
;
;  Print File Name
;
prfname:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,tfcb+1	;pt to first char
	mvi	b,8	;8 chars
	call	lfn1
	mvi	a,'.'
	call	lout
	mvi	b,3	;3 chars
	call	lfn1
	ret
lfn1:
	mov	a,m	;get char
	ani	7fh	;mask
	call	lout	;send to printer
	inx	h	;pt to next
	dcr	b	;count down
	jnz	lfn1
	ret
;
;  Print Separator
;
prdash:
	call	lprint
	db	' -- ',0
	ret
;
	if	timeok
;
;  Print Time
;
prtime:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,timebf	;pt to time stamp
	call	lpstr	;print
	ret
;
	endif
;
;  Print Header
;
prhdg:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,hbuf	;pt to heading
	call	lpstr	;print
	ret
;
;  UTILITIES
;	SBLANK  -- SKIP BLANKS PTED TO BY HL UNTIL NON-BLANK ENCOUNTERED; HL
;	SNBLANK -- SKIP NON-BLANKS PTED TO BY HL UNTIL BLANK OR EOL; HL
;	PRFN    -- PRINT FILE NAME PTED TO BY HL; AFFECT NOTHING
;

;
;  SKIP UNTIL NON-BLANK
;
SBLANK:
	MOV	A,M	; LOOK FOR BLANK
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	SBLANK
	DCX	H	; BACK UP
	RET

;
;  SKIP UNTIL BLANK OR EOL
;
SNBLANK:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	SNB1
	ORA	A	; EOL?
	JNZ	SNBLANK
SNB1:
	DCX	H	; BACK UP
	RET

;
;  PRINT FILE NAME PTED TO BY HL
;	OUTPUT TO CON:
;
PRFN:
	PUSH	H	; SAVE REGS
	PUSH	B
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; PRINT DISK
	ADI	'A'	; LETTER
	CALL	COUT
	MOV	A,C	; PRINT USER
	CALL	PADC
	CALL	PRINT
	DB	': ',0
	INX	H	; PT TO FILE NAME
	MVI	B,8	; PRINT NAME
	CALL	PRNT
	MVI	A,'.'	; DECIMAL
	CALL	COUT
	MVI	B,3	; PRINT TYPE
	CALL	PRNT
	POP	B	; GET REGS
	POP	H
	RET

;
;  PRINT CHARS PTED TO BY HL FOR B BYTES
;	OUTPUT TO CON:
;
PRNT:
	MOV	A,M	; GET CHAR
	CALL	COUT
	INX	H	; PT TO NEXT
	DCR	B	; COUNT DOWN
	JNZ	PRNT
	RET

;
;  BUFFERS
;
DISK:
	DS	1	; HOME DISK NUMBER
USER:
	DS	1	; HOME USER NUMBER
CDISK:
	DS	1	; CURRENT DISK NUMBER
CUSER:
	DS	1	; CURRENT USER NUMBER
CMDLNE:
	DS	2	; PTR TO COMMAND LINE STRING
NEXTCH:
	DS	2	; PTR TO NEXT CHAR IN MULTIFILE COMMAND LINE
FILECNT:
	DS	2	; COUNT OF NUMBER OF FILES RENAMED
SCRATCH:
	DS	2	; ADDRESS OF FIRST FREE BYTE
BCNT:
	DS	1	; NUMBER OF PAGES IN SCRATCH AREA
NTFCB:
	DS	36	; FCB FOR NEW FILE
;
;  PRINT3 Buffers
;
tfcb:
	ds	36	; FCB for current file
nxtblk:
	ds	2	; pointer to next block to load
nxtln:
	ds	2	; pointer to next line to read
lcount:
	ds	1	; count of text lines left on page
;
;  Stack
;
	DS	100	; STACK AREA
STACK:
	DS	2	; OLD STACK PTR

	END
